[DO NOT MERGE] POC: Add language-level tagged unions (enum union) and pattern matching (switch expressions) - #23744
[DO NOT MERGE] POC: Add language-level tagged unions (enum union) and pattern matching (switch expressions)#23744MetaLang wants to merge 5 commits into
Conversation
DMD perf check
Breakdown — compile hello.d
Breakdown — compile Phobos+67.1 M instructions: frontend +66.9 M (+1.83%), codegen +0.2 M (+0.01%)
All measurements
cc20c6d vs merge-base 7e0b115 · about these metrics |
| from.isFunction_Delegate_PtrToFunction() | ||
| ? MATCH.convert : MATCH.exact; | ||
| const isNullUnitVariant = from.toBasetype().ty == Tnull && | ||
| variant.payload.length == 0 && variant.ident == Identifier.idPool("None"); |
There was a problem hiding this comment.
Try to avoid comparing identifiers by string, chuck None into the table and do a pointer comparison instead.
| alias Parameters = Array!(Parameter); | ||
| alias Statements = Array!(Statement); | ||
| alias Catches = Array!(Catch); | ||
| inout(SwitchExp) isSwitchExp() { return op == EXP.switchExpression ? cast(typeof(return))this : null; } |
There was a problem hiding this comment.
That doesn't look like the right place for it.
|
Some of the implementation is certainly cleaner. It doesn't handle integer confusion, which is why I banned that. No handling of alias sequences and expansion into variants. Kinda important use case both in literature and in D code. You did not solve for the overlapped error. You support multiple values per variant, I assume that tuples will exist to define that into existance. It does not support by-ref, vs by-value this is a killer feature over library. It does not support chaining like it would via UFCS, this is a downgrade over library. It requires a lot of redundent tokens, you don't need case inside of the declaration, nor in switch expression. The extra combination of enum and union I very much dislike it. As we've learned from DIP1000 that is not a good thing. |
It's just vibe-coded LLM slop I banged out to show my vision for how such a feature would work.
I'm not completely sure what you're referring to. It will reject cases like: enum union Num
{
int,
long,
}
Num n = 0;Unless you disambiguate with a cast or literal syntax.
Ya, too complicated to implement for a POC, but it should be supported through .tupleof or something similar.
What's that?
The variants are "tuple-like", but currently don't have any relation to tuples. I don't think that's a necessity, but may be nice to have. The struct variants are really useful though.
Ya I'm not sure what to do about that because of the safety issues. Maybe with your fast DFA it'd be safe to support.
That feature is maybe a nice to have, but it's very easy to emulate with a switch expression inside a UFCS function and IMO doesn't add a whole lot.
Those are for readability/comprehensibility more than anything. It makes it more clear to the people reading and writing the code what's going on semantically, and I think that the case token before each variant might be necessary for disambiguation if you also wanna have member functions inside the body, but I may be misremembering. The case tokens in switch expressions are to match how you declare cases in the enum union body, and to make it more familiar for programmers who are used to the regular switch. But yeah they're unnecessary in terms of parsing.
I think it's good for signalling to the programmer what this construct does and how it works. It's like a union, but with an enumerated list of cases (and enums are traditionally used for the tag in a tagged union). Also note that Rust, Swift, C#, Zig and Odin all use the keywords enum and/or union (in Zig it's literally You could just as easily use |
Its an error for
I've got to go do that on my PR.
Booo functions, not analyzable! Not clean chaining of input ranges.
|
Add language-level tagged enum unions and switch-expression matching.\n\nFeatures:\n- Parse enum union declarations with named, tuple, record, alias, and bare-type variants.\n- Synthesize tagged storage, payload structs, variant factories, and destruction support.\n- Support implicit construction for unambiguous bare-type variants and reject duplicate or ambiguous cases.\n- Expand static foreach and static if enum-union cases in their correct semantic scope.\n- Add tuple and record pattern bindings, named/literal field checks, rest patterns, and guarded arms.\n- Diagnose non-union patterns, unmatched variants, invalid positional patterns, duplicate defaults, and the 256-variant limit.\n- Add frontend matrix-based usefulness checks for redundant arms, redundant defaults, guarded coverage, and exhaustiveness witnesses.\n- Add specification, runnable, compilable, and fail-compilation regression coverage.
Replace the obsolete record discard spelling with the supported syntax in the NetworkEvent example.
- add parser and AST support for enum union declarations, named/unit/record/bare variants, templates, static foreach, variant UDAs, and struct-equivalent declaration attributes - synthesize tagged storage, named factories, bare-variant constructors, and lifecycle support for payloads - add implicit bare-variant conversion with ambiguity diagnostics and preserve enum-union non-callability - implement enum-union switch expressions with positional, record, type, rest, guard, default, redundancy, and exhaustiveness handling - diagnose malformed switch patterns, duplicate cases, unknown bare types, oversized unions, and invalid variant declarations - prevent user constructors from reading this before whole-value initialization, with branch-aware constructor flow tracking - eliminate duplicate complex and imaginary transition deprecations from generated constructor parameters - document enum union grammar, construction, pattern matching, member declarations, and limitations - add runnable, compilable, and fail-compilation coverage for syntax, conversion, constructors, UDAs, attributes, diagnostics, patterns, and lifecycle behavior
Add enum-union pattern matching and construction refinements: - resolve bare and qualified type patterns, including identifier bindings - support payload bindings for named variants and share unit payload storage - diagnose self aliases, unknown bare types, no-argument constructors, and ambiguous bare conversions - resolve property-valued switch conditions and improve switch-expression formatting - allow arrow-arm switch expressions in statement position and diagnose discarded no-effect switches - preserve parser recovery for malformed switch expressions - add focused compile, fail, and runnable regression coverage
This PR adds language-level support for Rust/Swift-style tagged unions (called
enum union), and dedicated syntax for matching on their variants in the form of switch expressions.I wasn't satisfied with @rikkimax's approach in #23540, which is more in line with the structural approach taken by ML/Haskell, so I decided to implement it myself. And by myself, I mean an LLM - I wrote the spec, but it wrote all of the code and tests, and it was pretty quick and dirty. Consequently and unsurprisingly, most of the test runners are failing.
See enum_union_guide.md for an explanation of how the features work.
Included
@xoxorwr @limepoutine @Herringway